// Copyright 2011 Google Inc. All Rights Reserved.

/**
 * @fileoverview This is the root module for the net-score project.
 * @author gavaletz@google.com (Eric Gavaletz)
 */


goog.provide('developer');


goog.require('goog.array');
goog.require('goog.dom');
goog.require('goog.events');
goog.require('goog.json');
goog.require('goog.net.XhrIo');
goog.require('goog.ui.Button');
goog.require('goog.ui.Checkbox');
goog.require('goog.ui.FlatButtonRenderer');
goog.require('goog.ui.LabelInput');
goog.require('goog.ui.Textarea');
goog.require('page');
goog.require('results');
goog.require('track');


/**
 * Indicates that the visualization libraries have been loaded and that their
 * dependencies can start.
 *
 * @type {boolean}
 */
developer.visReady = false;


/**
 * Indicates that the data has been loaded and that their dependencies can
 * start.
 *
 * @type {boolean}
 */
developer.dataReady = false;


/**
 * Indicates that the results have finished being displayed.
 *
 * @type {boolean}
 */
developer.resultsDone = false;


/**
 * The table with the list of sites in it.
 *
 * @type {google.visualization.Table}
 */
developer.siteTable;


/**
 * The div for the table with the list of sites in it.
 *
 * @type {!Element}
 */
developer.resultsTableDisplayDiv;


/**
 * Displayed in the code snippit boxes when none or more than one site is
 * selected in the siteTable.
 *
 * @type {string}
 */
developer.defaultCode = nsi18n('devDefaultCode');


/**
 * Displayed when there are no sites to be displayed.
 *
 * @type {string}
 */
developer.emptyCode = nsi18n('devEmptyCode');


/**
 * For more information on this type see type DevSiteData struct in
 * gae/net-score/ns/developer.go
 *
 * @typedef {Object.<string, (string|number)>}
 */
developer.DevSiteData;


/**
 * A list of DevSiteData objects.
 *
 * @type {Array.<DevSiteData>}
 */
developer.data;


/**
 * Sets a master list of DevSiteData objects.
 *
 * @param {Array.<DevSiteData>} a the data being set from the server.
 */
developer.setData = function(a) {
  developer.data = a;

  //Change the date to something more human friendly.
  var max_i = developer.data.length;
  for (i = 0; i < max_i; i++) {
    developer.data[i]['DateFirstTest'] =
        new Date(developer.data[i]['DateFirstTest'] * 1000).toUTCString();
    developer.data[i]['DateLastTest'] =
        new Date(developer.data[i]['DateLastTest'] * 1000).toUTCString();
  }

  //Let everyone know that the data is ready to be used.
  developer.dataReady = true;

  //If the vis libs are loaded then go ahead and display the data.
  if (developer.visReady && !developer.resultsDone) {
    developer.displayResults();
  }
};
//used by the closure compiler to eport this function call
goog.exportProperty(window, 'developerSetData', developer.setData);


/**
 * An instance of goog.net.XhrIo for adding sites.
 *
 * @type {goog.net.XhrIo}
 */
developer.xhrAdd = new goog.net.XhrIo();


/**
 * The path to be used for adding sites.
 *
 * @type {string}
 */
developer.addPath = '/user/developerAddSite/';


/**
 * Sets the path for adding sites.  Usually just used from the server.
 *
 * @param {string} s the path to be used.
 */
developer.setAddPath = function(s) {
  developer.addPath = s;
};
//used by the closure compiler to eport this function call
goog.exportProperty(window, 'developerSetAddPath', developer.setAddPath);


/**
 * An instance of goog.net.XhrIo for removing sites.
 *
 * @type {goog.net.XhrIo}
 */
developer.xhrRm = new goog.net.XhrIo();


/**
 * The path to be used for removing sites.
 *
 * @type {string}
 */
developer.rmPath = '/user/developerRmSite/';


/**
 * Sets the path for removing sites.  Usually just used from the server.
 *
 * @param {string} s the path to be used.
 */
developer.setRmPath = function(s) {
  developer.rmPath = s;
};
//used by the closure compiler to eport this function call
goog.exportProperty(window, 'developerSetRmPath', developer.setRmPath);


/**
 * The path to be used for viewing results.
 *
 * @type {string}
 */
developer.resultsPath = '/user/developerResults/';


/**
 * Sets the path for viewing results.  Usually just used from the server.
 *
 * @param {string} s the path to be used.
 */
developer.setResultsPath = function(s) {
  developer.resultsPath = s;
};
//used by the closure compiler to eport this function call
goog.exportProperty(window, 'developerSetResultsPath',
    developer.setResultsPath);


/**
 * Removes a site from the list of sites.
 *
 * @param {number} i the index of the site to be removed.
 */
developer.rmData = function(i) {
  goog.array.removeAt(developer.data, i);
  goog.dom.removeChildren(developer.resultsTableDisplayDiv);
  if (developer.data.length > 0) {
    var dataObj = results.recordsToObj(developer.data);
    developer.siteTable = results.table(dataObj,
        developer.resultsTableDisplayDiv);
    results.addListener(developer.siteTable,
        goog.ui.Component.EventType.SELECT, developer.siteSelect);
    developer.code.setValue(developer.defaultCode);
    developer.url.setValue(developer.defaultCode);
  }
  else {
    developer.code.setValue(developer.emptyCode);
    developer.url.setValue(developer.emptyCode);
  }
};


/**
 * Adds a site to the list of sites.
 *
 * @param {DevSiteData} o the DevSiteData object to be added.
 */
developer.addData = function(o) {
  developer.data.push(o);
  goog.dom.removeChildren(developer.resultsTableDisplayDiv);
  var dataObj = results.recordsToObj(developer.data);
  developer.siteTable = results.table(dataObj,
      developer.resultsTableDisplayDiv);
  results.addListener(developer.siteTable, goog.ui.Component.EventType.SELECT,
      developer.siteSelect);
};


/**
 * http://code.google.com/apis/loader/
 *
 * This is some tricky stuff and the google.load is not well documented and does
 * some strange things if you are not careful.  Always list the callback
 * function in optional parameters.  See this message for an explination:
 *
 * http://groups.google.com/group/google-maps-api/msg/936506d83fea4458
 */
developer.loadLibs = function() {
  google.load('visualization', '1', {'callback': developer.visualizationLoaded,
    'packages': ['corechart', 'table']});
};


/**
 * The primary entry point for net-score.
 */
developer.start = function() {
  track.trackMe(track.trackingCode);
  developer.loadLibs();
};
//used by the closure compiler to eport this function call
goog.exportProperty(window, 'developerStart', developer.start);


/**
 * Callback function for loading the charts API JavaScript library.
 *
 * Sets the indicator variable letting other functions know that the charts API
 * is loaded and if the test results are complete we can start displaying the
 * results..
 */
developer.visualizationLoaded = function() {
  developer.visReady = true;
  if (developer.dataReady && !developer.resultsDone) {
    developer.displayResults();
  }
};


/**
 * Displays the results in the browser window.
 *
 * Once the data has been loaded this function does most of the heavy lifting
 * needed to display the results.
 */
developer.displayResults = function() {
  developer.resultsDone = true;
  page.developerSetup();

  var addHostDiv = goog.dom.createDom('div');
  goog.dom.appendChild(page.content, addHostDiv);
  developer.addHostButton = new goog.ui.Button(nsi18n('devNewSiteButton'),
      goog.ui.FlatButtonRenderer.getInstance());
  developer.addHostButton.render(addHostDiv);
  developer.addHostButton.setTooltip(nsi18n('devNewSiteTip'));
  //Tweak to make the button match the height of the text box
  goog.dom.setProperties(developer.addHostButton.getElement(),
      {'style': 'padding: 0 2px 0 2px;'});
  goog.events.listen(developer.addHostButton,
      goog.ui.Component.EventType.ACTION, developer.siteAdd);

  developer.hostNameInput = new goog.ui.LabelInput(nsi18n('devSiteURL'));
  developer.hostNameInput.render(addHostDiv);
  //Tweak to make the text box line up with the button
  goog.dom.setProperties(developer.hostNameInput.getElement(),
      {'style': 'margin: 2px;'});

  developer.resultsTableDisplayDiv = goog.dom.createDom('div',
      {'class': 'table'});
  var contentDiv = goog.dom.createDom('div', {},
      developer.resultsTableDisplayDiv);
  goog.dom.appendChild(page.content, contentDiv);
  if (developer.data.length > 0) {
    var dataObj = results.recordsToObj(developer.data);
    developer.siteTable = results.table(dataObj,
        developer.resultsTableDisplayDiv);
    results.addListener(developer.siteTable,
        goog.ui.Component.EventType.SELECT, developer.siteSelect);
  }

  //delete and data buttons
  var actionButtonsDiv = goog.dom.createDom('div');
  goog.dom.appendChild(page.content, actionButtonsDiv);
  developer.rmHostButton = new goog.ui.Button(nsi18n('devDeleteButton'),
      goog.ui.FlatButtonRenderer.getInstance());
  developer.rmHostButton.render(actionButtonsDiv);
  developer.rmHostButton.setTooltip(nsi18n('devDeleteTip'));
  goog.events.listen(developer.rmHostButton,
      goog.ui.Component.EventType.ACTION, developer.siteRm);
  developer.rmHostButton.setEnabled(false);

  developer.dataHostButton = new goog.ui.Button(nsi18n('devDataButton'),
      goog.ui.FlatButtonRenderer.getInstance());
  developer.dataHostButton.render(actionButtonsDiv);
  developer.dataHostButton.setTooltip(nsi18n('devDataTip'));
  goog.events.listen(developer.dataHostButton,
      goog.ui.Component.EventType.ACTION, developer.siteData);
  developer.dataHostButton.setEnabled(false);

  var visibleEmbedsDiv = goog.dom.createDom('div', {'class': 'message'},
      nsi18n('devVisTrack'));
  goog.dom.appendChild(page.content, visibleEmbedsDiv);
  developer.visibleEmbeds = new goog.ui.Checkbox(false);
  developer.visibleEmbeds.render(visibleEmbedsDiv);
  developer.visibleEmbeds.setLabel(
      developer.visibleEmbeds.getElement().parentNode);
  goog.events.listen(developer.visibleEmbeds,
      goog.ui.Component.EventType.CHANGE, developer.siteSelect);
  goog.dom.setProperties(developer.visibleEmbeds.getElement(),
      {'style': 'vertical-align: top; margin: 5px 0 10px 5px;'});

  //tracking code
  var codeDiv = goog.dom.createDom('div', {'class': 'message'},
      nsi18n('devCode'));
  goog.dom.appendChild(page.content, codeDiv);
  developer.code = new goog.ui.Textarea(developer.defaultCode);
  developer.code.render(page.content);
  developer.code.setEnabled(true);
  developer.code.setMinHeight(150);
  //Tweak to make the text box line up with the button
  goog.dom.setProperties(developer.code.getElement(),
      {'class': 'codeBlock'});

  //gadget url
  var urlDiv = goog.dom.createDom('div', {'class': 'message'},
      nsi18n('devURL'));
  goog.dom.appendChild(page.content, urlDiv);
  developer.url = new goog.ui.Textarea(developer.defaultCode);
  developer.url.render(page.content);
  developer.url.setEnabled(true);
  //Tweak to make the text box line up with the button
  goog.dom.setProperties(developer.url.getElement(),
      {'class': 'codeBlock'});

  //documentation link
  var deployDocLink = goog.dom.createDom('a',
      {'href': 'http://info.net-score.org/developer/code-deployment',
        'target': '_blank'}, 'info.net-score.org/developer');
  var infoDiv = goog.dom.createDom('div', {'class': 'message'},
      nsi18n('devDocInfo'), deployDocLink, '.');
  goog.dom.appendChild(page.content, infoDiv);

  if (developer.data.length == 0) {
    developer.code.setValue(developer.emptyCode);
    developer.url.setValue(developer.emptyCode);
  }
};


/**
 * Event handler called when one of the rows in the table of sites is selected.
 *
 * @param {goog.ui.Component.EventType} e the SELECT event.
 */
developer.siteSelect = function(e) {
  var selection = developer.siteTable.getSelection();
  if (selection.length == 1) {
    developer.dataHostButton.setEnabled(true);
    developer.rmHostButton.setEnabled(true);
    developer.setCode(selection[0]['row']);
  }
  else {
    //Track message to just select one.
    developer.dataHostButton.setEnabled(false);
    developer.rmHostButton.setEnabled(false);
    if (developer.data.length == 0) {
      developer.code.setValue(developer.emptyCode);
      developer.url.setValue(developer.emptyCode);
    }
    else {
      developer.code.setValue(developer.defaultCode);
      developer.url.setValue(developer.defaultCode);
    }
  }
};


/**
 * Event handler called when the button for adding a site is clicked.
 *
 * @param {goog.ui.Component.EventType} e the ACTION event.
 */
developer.siteAdd = function(e) {
  developer.addHostButton.setEnabled(false);
  var url = developer.hostNameInput.getValue();
  var uri = new goog.Uri(url);
  if (!uri.hasDomain() || !uri.hasScheme() || (uri.getScheme() != 'http' &&
          uri.getScheme() != 'https')) {
    alert(nsi18n('devAlertURL1'));
    developer.hostNameInput.reset();
    developer.addHostButton.setEnabled(true);
    return;
  }
  if (!uri.hasPath()) {
    uri.setPath('');
  }
  if (uri.hasPort() || uri.hasQuery() || uri.hasUserInfo()) {
    alert(nsi18n('devAlertURL2'));
  }
  var goodURL = uri.getScheme() + '://' + uri.getDomain() + '/' + uri.getPath();
  console.log(goodURL);
  developer.xhrAdd.send(developer.addPath, 'POST', goodURL,
      {'content-type': 'text/plain'});
};


/**
 * Callback for adding the site.
 *
 * @this {goog.net.XhrIo}
 * @param {goog.events.Event} e The event that triggered this call.
 */
developer.siteAddDone = function(e) {
  var response = this.getResponseText();
  var data = goog.json.unsafeParse(response);
  developer.addData(data);
  developer.hostNameInput.reset();
  developer.addHostButton.setEnabled(true);
  developer.code.setValue(developer.defaultCode);
  developer.url.setValue(developer.defaultCode);
};
//Register this function call with the site adder
goog.events.listen(developer.xhrAdd, goog.net.EventType.COMPLETE,
    developer.siteAddDone);


/**
 * Event handler called when the button for viewing site data is clicked.
 *
 * @param {goog.ui.Component.EventType} e the ACTION event.
 */
developer.siteData = function(e) {
  var selection = developer.siteTable.getSelection();
  var index = selection[0]['row'];
  var url = developer.resultsPath + developer.data[index]['DevKey'];
  window.open(url);
};


/**
 * Event handler called when the button for removing a site is clicked.
 *
 * @param {goog.ui.Component.EventType} e the ACTION event.
 */
developer.siteRm = function(e) {
  developer.rmHostButton.setEnabled(false);
  developer.dataHostButton.setEnabled(false);
  var selection = developer.siteTable.getSelection();
  developer.rmIndex = selection[0]['row'];
  var devKey = developer.data[developer.rmIndex]['DevKey'];
  var sure = confirm(nsi18n('devConfirmDel1') + devKey +
      nsi18n('devConfirmDel2'));
  if (sure) {
    var parts = devKey.split('-');
    var index = parts[parts.length - 1];
    developer.xhrRm.send(developer.rmPath + index, 'GET');
    developer.siteTable.setSelection();
  }
  else {
    developer.rmHostButton.setEnabled(true);
    developer.dataHostButton.setEnabled(true);
  }
};


/**
 * Callback for adding the site.
 *
 * @this {goog.net.XhrIo}
 * @param {goog.events.Event} e The event that triggered this call.
 */
developer.siteRmDone = function(e) {
  var response = this.getResponseText();
  if (response == 'OK') {
    developer.rmData(developer.rmIndex);
    developer.rmIndex = null;
  }
  else {
    datastore.logError('developerError', 'response = ' + response);
  }
};
//Register this function call with the site remover
goog.events.listen(developer.xhrRm, goog.net.EventType.COMPLETE,
    developer.siteRmDone);


/**
 * Displays the code snippits for the selected site.
 *
 * @param {number} i the selected site.
 */
developer.setCode = function(i) {
  if (developer.visibleEmbeds.getChecked()) {
    developer.url.setValue('http://net-score.org/egg.xml');
    var key = developer.data[i]['DevKey'];
    var snippit = '<!--begin netscore tag -->\n' +
        '  <div id="net-score-egg"></div>\n' +
        '  <script type="text/javascript">\n' +
        '  var nsd = document.getElementById(\'net-score-egg\');\n' +
        '  var nsf = document.createElement(\'iframe\');\n' +
        '  nsf.setAttribute(\'id\', \'nsf\');\n' +
        '  nsf.setAttribute(\'frameborder\', \'0\');\n' +
        '  nsf.setAttribute(\'height\', \'300px\');\n' +
        '  nsf.setAttribute(\'width\', \'300px\');\n' +
        '  nsf.setAttribute(\'scrolling\', \'no\');\n' +
        '  document.body.appendChild(nsf);\n' +
        '  nsd.appendChild(nsf);\n' +
        '  nsf.setAttribute(\'src\', \'http://net-score.org/egg?devKey=' + key +
        '\');\n  </script>\n<!--end netscore tag -->';
    developer.code.setValue(snippit);
  }
  else {
    developer.url.setValue('http://net-score.org/feather.xml');
    var key = developer.data[i]['DevKey'];
    var snippit = '<!--begin netscore tag -->\n' +
        '  <script type="text/javascript">\n' +
        '  var nsf = document.createElement(\'iframe\');\n' +
        '  nsf.setAttribute(\'id\', \'nsf\');\n' +
        '  nsf.setAttribute(\'style\', \'visibility:hidden\');\n' +
        '  document.body.appendChild(nsf);\n' +
        '  nsf.setAttribute(\'src\', \'http://net-score.org/feather?devKey=' +
        key + '\');\n  </script>\n<!--end netscore tag -->';
    developer.code.setValue(snippit);
  }
};
